﻿extern alias util;

using System;
using System.Collections.Generic;
using System.ComponentModel;

using JetBrains.DocumentModel;
using JetBrains.ReSharper.Psi.CSharp;
using util::JetBrains.Application.Progress;
using JetBrains.ReSharper.Feature.Services.CodeCleanup;
using JetBrains.ReSharper.Feature.Services.CSharp.CodeCleanup;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.GeneratedCode;

namespace FinlogiK.ReSharperContrib
{
	/// <summary>
	/// 	Code cleanup module for arranging static qualifiers.
	/// </summary>
	[CodeCleanupModule(ModulesBefore = new[] { typeof(ArrangeThisQualifier), typeof(OptimizeUsings) })]
	public sealed class ArrangeStaticQualifierCleanupModule : ICodeCleanupModule
	{
		#region Fields

		private readonly ArrangeStaticQualifierCodeCleanupOptionDescriptor _descriptor = new ArrangeStaticQualifierCodeCleanupOptionDescriptor();

		#endregion

		#region Constructors

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "ArrangeStaticQualifierCleanupModule" /> class.
		/// </summary>
		public ArrangeStaticQualifierCleanupModule()
		{
		}

		#endregion

		#region ICodeCleanupModule Members

		/// <summary>
		/// 	Get the collection of option descriptors
		/// </summary>
		/// <value></value>
		public ICollection<CodeCleanupOptionDescriptor> Descriptors
		{
			get
			{
				return new CodeCleanupOptionDescriptor[] { this._descriptor };
			}
		}

		/// <summary>
		/// 	Gets a value indicating whether the module is available on selection, or on the whole file.
		/// </summary>
		/// <value>
		/// 	The is available on selection.
		/// </value>
		public bool IsAvailableOnSelection
		{
			get
			{
				return false;
			}
		}

        /// <summary>
        /// Process clean-up on file.
        /// </summary>
        /// <param name = "sourceFile">
        /// The project file to process.
        /// </param>
        /// <param name = "rangeMarker">
        /// The range marker to process.
        /// </param>
        /// <param name = "profile">
        /// The code cleanup settings to use.
        /// </param>
        /// <param name = "progressIndicator">
        /// The progress indicator.
        /// </param>
	    public void Process(IPsiSourceFile sourceFile, IRangeMarker rangeMarker, CodeCleanupProfile profile, IProgressIndicator progressIndicator)
	    {
            if (profile.GetSetting(this._descriptor))
            {
                if (!sourceFile.ToProjectFile().IsGeneratedFile())
                {
                    var file = sourceFile.GetPsiFile<CSharpLanguage>() as ICSharpFile;

                    if (file != null)
                    {
                        var solution = sourceFile.GetSolution();
                        PsiManager.GetInstance(solution).DoTransaction(() => ArrangeStaticQualifierCleanupModule.ProcessFile(file), "Code cleanup");
                    }
                }
            }
	    }

	    /// <summary>
        /// Gets the language this module can operate.
        /// </summary>
        /// <value>
        /// The language type.
        /// </value>
	    public PsiLanguageType LanguageType
	    {
            get
            {
                return CSharpLanguage.Instance;
            }
	    }

	    /// <summary>
		/// 	Get default setting for given profile type
		/// </summary>
		/// <param name = "profile"></param>
		/// <param name = "profileType"></param>
		public void SetDefaultSetting(CodeCleanupProfile profile, CodeCleanup.DefaultProfileType profileType)
		{
			profile.SetSetting(this._descriptor, true);
		}

	    /// <summary>
        /// Check if this module can handle given project file.
        /// </summary>
        /// <param name = "sourceFile">
        /// The project file to check.
        /// </param>
        /// <returns>
        /// <c>True.</c>if the project file is available; otherwise 
        /// <c>False.</c>.
        /// </returns>
	    public bool IsAvailable(IPsiSourceFile sourceFile)
	    {
            return sourceFile.GetPsiFile<CSharpLanguage>() != null;
	    }

	    #endregion

	    #region Methods

	    private static IEnumerable<IReferenceExpression> GetStaticReferencesWithoutQualifier(ICSharpFile file)
		{
			var invalidReferences = new List<IReferenceExpression>();
			var processor = new RecursiveElementProcessor<IReferenceExpression>(referenceExpression =>
			{
				if (ArrangeStaticQualifierHelper.IsReferenceToStaticMemberWithoutQualifier(referenceExpression))
				{
					invalidReferences.Add(referenceExpression);
				}
			});

			file.ProcessDescendants(processor);

			return invalidReferences;
		}

	    private static void ProcessFile(ICSharpFile file)
	    {
	        IEnumerable<IReferenceExpression> invalidReferences =
	            ArrangeStaticQualifierCleanupModule.GetStaticReferencesWithoutQualifier(file);

	        foreach (IReferenceExpression referenceExpression in invalidReferences)
	        {
	            ArrangeStaticQualifierHelper.AddStaticQualifier(referenceExpression);
	        }
	    }

	    #endregion

		#region Nested Types

		[Category("FinlogiK Contrib")]
		[DefaultValue(true)]
		[DisplayName("Arrange static qualifier")]
		private sealed class ArrangeStaticQualifierCodeCleanupOptionDescriptor : CodeCleanupBoolOptionDescriptor
		{
			#region Constructors

			internal ArrangeStaticQualifierCodeCleanupOptionDescriptor()
				: base("FinlogiKArrangeStaticQualifier")
			{
			}

			#endregion
		}

		#endregion
	}
}